"""
数据规整 聚合 合并和重塑
层次化索引 使你在一个轴上拥有多个两个以上索引级别 抽象来说 使得你能以低纬度形式处理高纬度数据

# 创建一个 Series 并用一个列表或数组组成的列表作为索引
data = pd.Series(np.random.randn(9),index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'],[1, 2, 3, 1, 3, 1, 2, 2, 3]])
print(data)
print(data.index) # 经过美化的带有MultiIndex的索引Series 索引之间的间隔 表示 直接使用上面的标签

# 对于一个层次化索引的对象 选取数据子集的操作
data['b'] # 选取标签为b的子集
data['b':'c'] # 选取标签为b到c的子集
data.loc[['b', 'd']] # 选取标签为b和d的子集
data.loc[:,2] # 选取标签为2的子集

# 层次化索引在数据重塑和基于分组的操作 可以通过 unstack()方法将这段数据重新安排到一个DataFrame中
print(data.unstack()) # 将索引中的标签分别放在行和列上
print(data.unstack().stack()) # 将索引中的标签分别放在行和列上


# 为DataFrame的索引和列设置名称，以提高数据的可读性和易于理解
frame.index.names = ['key1', 'key2']
frame.columns.names = ['state', 'color']
# 打印设置了索引和列名称后的DataFrame
print(frame)

# 选取列索引 轻松选取列分组
print(frame['Ohio'])

# 重排与分级排序 重新调整某条轴上各级别的顺序 或根据指定级别上的值对数据进行排序 swaplevel() 接受两个级别编号或名称 并返回一个互换了级别的新对象
# 使用swaplevel方法交换数据框中的两个级别,key1和key2,但不改变原始数据框的结构
# print(frame.swaplevel('key1', 'key2')) # 交换了key1和key2

# sort_index 方法根据单个级别中的值对数据进行排序。这里是对第二个级别（level=1）进行排序
# 排序结果是按照指定级别中的值进行字母顺序排列
print(frame.sort_index(level=1))

# 这里先使用swaplevel方法交换第一个级别（0）和第二个级别（1）,然后使用sort_index对新的第一个级别（0）进行排序
# 最终结果是先交换级别再按新级别的值进行字母排序
print(frame.swaplevel(0, 1).sort_index(level=0))

# 对于一个DataFrame，每条轴都可以有分层索引
# 创建一个DataFrame对象，使用给定的数据、索引和列标签
# 这里展示了如何使用numpy数组和多级索引来构造一个复杂的DataFrame结构
frame = pd.DataFrame(np.arange(12).reshape((4, 3)), index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]], columns=[['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']])
# 打印初始状态的DataFrame
print(frame)

# 许多对DataFrame和Series的描述和汇总统计都有一个level选项，它用于指定在某条轴上求和的级别。再以上面那个DataFrame为例，我们可以根据行或列上的级别来进行求和：
# 根据'key2'层级对DataFrame进行分组并计算每组的和
print(frame.sum(level='key2'))
# 根据'color'层级对DataFrame进行分组，并沿列方向计算每组的和
print(frame.sum(level='color', axis=1))

frame = pd.DataFrame({'a': range(7), 'b': range(7, 0, -1),'c': ['one', 'one', 'one', 'two', 'two','two', 'two'],'d': [0, 1, 2, 0, 1, 2, 3]})
# print(frame)

# DataFrame的set_index函数会将其一个或多个列转换为行索引，并创建一个新的DataFrame：
frame2 = frame.set_index(['c', 'd']) # 设置索引
print(frame2)

# 默认情况下 那些列会从DataFrame中被移除 但也可以将其保留下来
# frame3 = frame.set_index(['c', 'd'], drop=False)
# print(frame3)

# reset_index 跟set_index相反，它将索引重置为默认的整数索引
frame4 = frame2.reset_index()
print(frame4)

合并数据集
pandas 对象中的数据可以通过一些方式进行合并
pandas.merge可根据一个或多个键将不同DataFrame中的行进行连接起来
pandas.concat可以沿着一条轴将多个对象堆叠到一起
实例方法combine_first可以将重复数据拼接在一起，用一个对象中的值填充另一个对象中的缺失值。

数据库风格的DataFrame合并
数据集的合并merge 或连接 join 运算是通过一个或多个键将行连接起来 这些运算是关系型数据库（基于SQL）的核心。pandas的merge函数是对数据应用这些算法的主要切入点

# 这是一种多对一的合并。df1中的数据有多个被标记为a和b的行，而df2中key列的每个值则仅对应一行。对这些对象调用merge即可得到：
print(pd.merge(df1, df2))

# 当没有指明要用哪个列进行连接 如果没有指定 merge 就会将重叠列的列名作为连接键
print(pd.merge(df1, df2, on='key'))

# 创建一个名为df3的数据框，包含两列'lkey'和'data1'，其中'lkey'列的值用于后续的合并操作
df3 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],'data1': range(7)})
# 创建一个名为df4的数据框，包含两列'rkey'和'data2'，其中'rkey'列的值用于与df3进行合并操作
df4 = pd.DataFrame({'rkey': ['a', 'b', 'd'], 'data2': range(3)})

# 将df3和df4根据指定的键进行合并操作，'left_on'和'right_on'参数分别指定了用于合并的列
# 这里没有指定合并方式，默认为内连接（inner join），即只保留两个数据框中都有匹配项的行
pd.merge(df3, df4, left_on='lkey', right_on='rkey')

# 可能你已经注意到了，结果里面c和d以及与之相关的数据消失了。默认情况下，merge做的是“内连接”；结果中的键是交集。其他方式还有”left”、”right”以及”outer”。外连接求取的是键的并集，组合了左连接和右连接的效果：
pd.merge(df3, df4, how='outer') # 外连接 求取的是键的并集 组合了左连接和右连接的效果

# inner
# 使用两个表都有的键
# left
# 使用左表中所有的键
# right
# 使用右表中所有的键
# outer
# 使用两个表中所有的键


df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'], 'data1': range(6)})
df2 = pd.DataFrame({'key': ['a', 'b', 'd'], 'data2': range(3)})
print(df1)
print(df2)

pd.merge(df1, df2, on='key', how='left') # 左连接 使用左表中所有的键
# 多对多连接产生的是行的笛卡尔积 由于左边的DataFrame有3个"b"行 右边有2个"b"行 因此结果有6行b 连接方式只影响出现在结果中的不同的键的值
pd.merge(df1, df2, on='key', how='inner') # 内连接 求取的是键的交集

# 要多个键进行合并 传入一个由列名组成的列表即可
# 创建左数据框，包含key1和key2两列作为合并键，lval列为左侧数据
left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'], 'key2': ['one', 'two', 'one'], 'lval': [1, 2, 3]})

# 创建右数据框，包含key1和key2两列作为合并键，rval列为右侧数据
right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'], 'key2': ['one', 'one', 'one', 'two'], 'rval': [4, 5, 6, 7]})

# 使用merge函数将left和right数据框进行外连接，依据key1和key2两列
# how='outer'指定执行全外连接，以包含所有记录，无论是否匹配
pd.merge(left, right, on=['key1', 'key2'], how='outer')
# 结果中会出现哪些键组合取决所选的合并方式 多个键形成一系列元组 并将其当做单个连接键
# 对于合并运算需要考虑的最后一个问题是对重复列名的处理。虽然你可以手工处理列名重叠的问题（查看前面介绍的重命名轴标签），但merge有一个更实用的suffixes选项，用于指定附加到左右两个DataFrame对象的重叠列名上的字符串

pd.merge(left,right,on='key1')
pd.merge(left, right, on='key1', suffixes=('_left', '_right'))

left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],'value': range(6)})
right1 = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])

print(left1)
print(right1)
pd.merge(left1, right1, left_on='key', right_index=True)
# 由于默认的merge方法是求取连接键的交集,因此你可以通过外连接的方式得到它们的并集
pd.merge(left1, right1, left_on='key', right_index=True, how='outer')

用于重新排列表格型数据的基础运算。这些函数也称作重塑（reshape）或轴向旋转（pivot）运算。
层次化索引为DataFrame数据的重排任务提供了一种具有良好一致性的方式。主要功能有二：

stack：将数据的列“旋转”为行。
unstack：将数据的行“旋转”为列。


"""
import numpy as np
import pandas as pd

data = pd.DataFrame(np.arange(6).reshape((2, 3)),index = pd.Index(['Ohio', 'Colorado'], name='state'),columns = pd.Index(['one', 'two', 'three'],name = 'number'))
print(data)

# 对数据使用stack方法即可将列转换为行 得到一个Series对象
result = data.stack()
print(result)

# 对一个层次化索引的Series 可以用unstack方法进行重排为一个DataFrame
result.unstack()
print(result)

# unstack 操作的是最内层 传入分层级别的编号或名称即可对其他级别进行unstack操作
# 将result DataFrame按照索引的第0层进行解堆叠，这会将内部索引提升为列
result.unstack(0)

# 将result DataFrame按照名为'state'的索引层进行解堆叠，同样将这一层索引转换为列
result.unstack('state')







if __name__ == '__main__':
    pass
